کاوش الگوریتم اجماع توزیعشده Raft، اصول اساسی، مراحل عملیاتی، ملاحظات پیادهسازی و کاربردهای واقعی آن برای ساخت سیستمهای مقاوم و مقیاسپذیر جهانی.
تسلط بر اجماع توزیعشده: نگاهی عمیق به پیادهسازی الگوریتم Raft برای سیستمهای جهانی
در دنیای فزاینده متصل امروزی ما، سیستمهای توزیعشده ستون فقرات تقریباً تمامی خدمات دیجیتال، از پلتفرمهای تجارت الکترونیک و مؤسسات مالی گرفته تا زیرساختهای رایانش ابری و ابزارهای ارتباطی بلادرنگ، هستند. این سیستمها با توزیع بار کاری و دادهها در میان چندین ماشین، مقیاسپذیری، دسترسیپذیری و انعطافپذیری بینظیری ارائه میدهند. با این حال، این قدرت با یک چالش مهم همراه است: اطمینان از توافق همه اجزا بر روی وضعیت سیستم، حتی در مواجهه با تأخیرهای شبکه، خرابیهای گرهها و عملیات همزمان. این مشکل اساسی با عنوان اجماع توزیعشده شناخته میشود.
دستیابی به اجماع در یک محیط توزیعشده ناهمزمان و مستعد خطا، به شدت پیچیده است. برای دههها، Paxos الگوریتم غالب برای حل این چالش بود که به دلیل استحکام نظریاش مورد احترام بود، اما اغلب به دلیل پیچیدگی و دشواری در پیادهسازی مورد انتقاد قرار میگرفت. سپس Raft آمد، الگوریتمی که با هدف اصلی: فهمپذیری طراحی شده بود. Raft در نظر دارد که از نظر تحمل خطا و عملکرد معادل Paxos باشد، اما به گونهای ساختار یافته است که درک و توسعه بر روی آن برای توسعهدهندگان بسیار آسانتر است.
این راهنمای جامع به عمق الگوریتم Raft میپردازد و اصول بنیادی، مکانیسمهای عملیاتی، ملاحظات پیادهسازی عملی و نقش حیاتی آن در ساخت برنامههای توزیعشده جهانی و مقاوم را بررسی میکند. چه یک معمار باتجربه باشید، چه یک مهندس سیستمهای توزیعشده، یا یک توسعهدهنده که آرزوی ساخت سرویسهای با دسترسی بالا را دارد، درک Raft گامی اساسی در جهت تسلط بر پیچیدگیهای محاسبات مدرن است.
نیاز ضروری به اجماع توزیعشده در معماریهای مدرن
یک پلتفرم تجارت الکترونیک جهانی را تصور کنید که میلیونها تراکنش را در ثانیه پردازش میکند. دادههای مشتری، سطوح موجودی، وضعیت سفارشها – همه باید در مراکز داده متعدد و در سراسر قارهها ثابت بمانند. دفتر کل یک سیستم بانکی، که در چندین سرور توزیع شده است، حتی یک لحظه اختلاف نظر در مورد موجودی حساب را تحمل نمیکند. این سناریوها اهمیت حیاتی اجماع توزیعشده را برجسته میکنند.
چالشهای ذاتی سیستمهای توزیعشده
سیستمهای توزیعشده، به دلیل ماهیت خود، چالشهای بیشماری را معرفی میکنند که در برنامههای یکپارچه (Monolithic) وجود ندارند. درک این چالشها برای درک زیبایی و ضرورت الگوریتمهایی مانند Raft حیاتی است:
- خطاهای جزئی: برخلاف یک سرور واحد که یا کار میکند یا به طور کامل از کار میافتد، یک سیستم توزیعشده میتواند برخی گرههایش دچار مشکل شوند در حالی که بقیه به کار خود ادامه میدهند. ممکن است یک سرور از کار بیفتد، اتصال شبکه آن قطع شود، یا دیسک آن خراب شود، در حالی که بقیه کلاستر همچنان فعال باقی میماند. سیستم باید با وجود این خطاهای جزئی به درستی عمل کند.
- جداسازی شبکه: شبکه اتصالدهنده گرهها همیشه قابل اعتماد نیست. جداسازی شبکه زمانی رخ میدهد که ارتباط بین زیرمجموعههایی از گرهها قطع شود، و این گونه به نظر برسد که برخی گرهها از کار افتادهاند، حتی اگر هنوز در حال اجرا باشند. حل این "دوپارگی مغز" (split-brain) سناریوها، که در آن بخشهای مختلف سیستم به طور مستقل بر اساس اطلاعات قدیمی یا ناسازگار عمل میکنند، یک مشکل اصلی اجماع است.
- ارتباطات ناهمزمان: پیامها بین گرهها میتوانند تأخیر داشته باشند، دوباره مرتب شوند یا به طور کامل از بین بروند. هیچ ساعت جهانی یا تضمینی در مورد زمان تحویل پیام وجود ندارد، که ایجاد یک ترتیب ثابت از رویدادها یا یک وضعیت قطعی سیستم را دشوار میکند.
- همزمانی: چندین گره ممکن است تلاش کنند تا یک قطعه داده را بهروزرسانی کنند یا اقدامات را به طور همزمان آغاز کنند. بدون مکانیزمی برای هماهنگ کردن این عملیات، تضادها و ناسازگاریها اجتنابناپذیر هستند.
- تأخیر غیرقابل پیشبینی: به ویژه در استقرارهای توزیعشده جهانی، تأخیر شبکه میتواند به طور قابل توجهی متفاوت باشد. عملیاتی که در یک منطقه سریع هستند، ممکن است در منطقه دیگری کند باشند و بر فرآیندهای تصمیمگیری و هماهنگی تأثیر بگذارند.
چرا اجماع سنگ بنای قابلیت اطمینان است
الگوریتمهای اجماع یک بلوک ساختمانی اساسی برای حل این چالشها ارائه میدهند. آنها مجموعهای از اجزای غیرقابل اعتماد را قادر میسازند تا به طور جمعی به عنوان یک واحد واحد، بسیار قابل اعتماد و منسجم عمل کنند. به طور خاص، اجماع به دستیابی به موارد زیر کمک میکند:
- تکثیر ماشین حالت (SMR): ایده اصلی پشت بسیاری از سیستمهای توزیعشده تحملپذیر خطا. اگر همه گرهها بر روی ترتیب عملیات توافق داشته باشند، و اگر هر گره در همان حالت اولیه شروع کند و آن عملیات را به همان ترتیب اجرا کند، آنگاه همه گرهها به همان حالت نهایی خواهند رسید. اجماع مکانیزمی است برای توافق بر این ترتیب جهانی عملیات.
- دسترسی بالا: با اجازه دادن به یک سیستم برای ادامه فعالیت حتی اگر اقلیتی از گرهها از کار بیفتند، اجماع تضمین میکند که سرویسها قابل دسترسی و کارآمد باقی میمانند و زمان توقف را به حداقل میرساند.
- سازگاری داده: این امر تضمین میکند که تمامی کپیهای داده همگامسازی شده باقی میمانند، از بهروزرسانیهای متناقض جلوگیری میکند و اطمینان میدهد که کلاینتها همیشه جدیدترین و صحیحترین اطلاعات را میخوانند.
- تحمل خطا: سیستم میتواند تعداد معینی از خرابیهای گره دلخواه (معمولاً خرابیهای سقوطی) را تحمل کند و بدون دخالت انسانی به پیشرفت ادامه دهد.
معرفی Raft: رویکردی فهمپذیر به اجماع
Raft از دنیای دانشگاهی با هدفی روشن پدیدار شد: قابل دسترس ساختن اجماع توزیعشده. نویسندگان آن، دیهگو اونگارو و جان آسترهاوت، Raft را به صراحت برای فهمپذیری طراحی کردند، با هدف فعالسازی پذیرش گستردهتر و پیادهسازی صحیح الگوریتمهای اجماع.
فلسفه طراحی اصلی Raft: فهمپذیری در اولویت
Raft مشکل پیچیده اجماع را به چندین زیرمسئله نسبتاً مستقل تقسیم میکند که هر یک دارای مجموعه قوانین و رفتارهای خاص خود هستند. این ماژولار بودن به طور قابل توجهی به درک کمک میکند. اصول طراحی کلیدی عبارتند از:
- رویکرد رهبر-محور: برخلاف برخی دیگر از الگوریتمهای اجماع که در آنها همه گرهها به طور مساوی در تصمیمگیری شرکت میکنند، Raft یک رهبر واحد را تعیین میکند. رهبر مسئول مدیریت لاگ تکثیر شده و هماهنگی تمامی درخواستهای کلاینت است. این امر مدیریت لاگ را ساده میکند و پیچیدگی تعاملات بین گرهها را کاهش میدهد.
- رهبر قدرتمند: رهبر دارای اختیارات نهایی برای پیشنهاد ورودیهای جدید لاگ و تعیین زمان commit آنها است. پیروان به طور غیرفعال لاگ رهبر را تکثیر میکنند و به درخواستهای رهبر پاسخ میدهند.
- انتخابات قطعی: Raft از یک مهلت زمانی انتخاب تصادفی استفاده میکند تا اطمینان حاصل شود که به طور معمول فقط یک نامزد در یک دوره انتخاباتی معین به عنوان رهبر ظاهر میشود.
- سازگاری لاگ: Raft ویژگیهای سازگاری قوی را بر روی لاگ تکثیر شده خود اعمال میکند، و اطمینان میدهد که ورودیهای commit شده هرگز بازگردانده نمیشوند و تمامی ورودیهای commit شده در نهایت در تمامی گرههای موجود ظاهر میشوند.
مقایسهای کوتاه با Paxos
قبل از Raft، Paxos استاندارد بالفعل برای اجماع توزیعشده بود. اگرچه قدرتمند بود، اما Paxos به شدت دشوار است که به درستی فهمیده و پیادهسازی شود. طراحی آن، که نقشها (پیشنهاد دهنده، پذیرنده، یادگیرنده) را جدا میکند و اجازه میدهد چندین رهبر به طور همزمان وجود داشته باشند (اگرچه فقط یکی میتواند یک مقدار را commit کند)، میتواند منجر به تعاملات پیچیده و موارد خاص شود.
Raft، در مقابل، فضای حالت را ساده میکند. این الگوریتم یک مدل رهبر قوی را اعمال میکند، که در آن رهبر مسئول تمام تغییرات لاگ است. Raft نقشها (رهبر، پیرو، نامزد) و انتقال بین آنها را به وضوح تعریف میکند. این ساختار رفتار Raft را شهودیتر و استدلالپذیرتر میکند، که منجر به اشکالات پیادهسازی کمتر و چرخههای توسعه سریعتر میشود. بسیاری از سیستمهای واقعی که در ابتدا با Paxos مشکل داشتند، با پذیرش Raft به موفقیت دست یافتهاند.
سه نقش اساسی در Raft
در هر زمان مشخص، هر سرور در یک کلاستر Raft در یکی از سه حالت زیر قرار دارد: رهبر، پیرو، یا نامزد. این نقشها انحصاری و پویا هستند، و سرورها بر اساس قوانین و رویدادهای خاص بین آنها تغییر حالت میدهند.
۱. پیرو
- نقش غیرفعال: پیروها منفعلترین حالت در Raft هستند. آنها صرفاً به درخواستهای رهبران و نامزدها پاسخ میدهند.
-
دریافت ضربان قلب: یک پیرو انتظار دارد در فواصل زمانی منظم، ضربان قلب (AppendEntries RPCهای خالی) را از رهبر دریافت کند. اگر یک پیرو در یک دوره
election timeoutخاص، ضربان قلب یا یک AppendEntries RPC دریافت نکند، فرض میکند که رهبر از کار افتاده است و به حالت نامزد تغییر میکند. - رایگیری: در طول یک انتخابات، یک پیرو در هر دوره حداکثر به یک نامزد رای خواهد داد.
- تکثیر لاگ: پیروها ورودیهای لاگ را طبق دستور رهبر به لاگ محلی خود اضافه میکنند.
۲. نامزد
- آغاز انتخابات: وقتی یک پیرو زمانش به پایان میرسد (از رهبر خبری نمیشنود)، به حالت نامزد تغییر میکند تا یک انتخابات جدید را آغاز کند.
-
خود-رأیدهی: یک نامزد
current termخود را افزایش میدهد، به خودش رأی میدهد و RPCهایRequestVoteرا به تمام سرورهای دیگر در کلاستر ارسال میکند. - برنده شدن در انتخابات: اگر یک نامزد در همان دوره از اکثریت سرورهای کلاستر رأی دریافت کند، به حالت رهبر تغییر میکند.
- کنارهگیری: اگر یک نامزد سرور دیگری را با دوره بالاتر کشف کند، یا اگر یک AppendEntries RPC از یک رهبر قانونی دریافت کند، به حالت پیرو بازمیگردد.
۳. رهبر
- اختیار واحد: در هر زمان معین (برای یک دوره معین)، تنها یک رهبر در کلاستر Raft وجود دارد. رهبر مسئول تمامی تعاملات کلاینت، تکثیر لاگ و تضمین سازگاری است.
-
ارسال ضربان قلب: رهبر به صورت دورهای RPCهای
AppendEntries(ضربان قلب) را به تمامی پیروها ارسال میکند تا اقتدار خود را حفظ کرده و از انتخابات جدید جلوگیری کند. - مدیریت لاگ: رهبر درخواستهای کلاینت را میپذیرد، ورودیهای جدید لاگ را به لاگ محلی خود اضافه میکند و سپس این ورودیها را به تمامی پیروها تکثیر میکند.
- تعهد (Commitment): رهبر تصمیم میگیرد چه زمانی یک ورودی با اطمینان به اکثریت سرورها تکثیر شده و میتواند به ماشین حالت commit شود.
-
کنارهگیری: اگر رهبر سروری را با
termبالاتر کشف کند، فوراً کنارهگیری کرده و به یک پیرو بازمیگردد. این امر تضمین میکند که سیستم همیشه با بالاترین دوره شناخته شده پیشرفت میکند.
فازهای عملیاتی Raft: یک مرور جامع
Raft از طریق یک چرخه پیوسته از انتخاب رهبر و تکثیر لاگ عمل میکند. این دو مکانیسم اصلی، در کنار ویژگیهای ایمنی حیاتی، تضمین میکنند که کلاستر سازگاری و تحمل خطا را حفظ میکند.
۱. انتخاب رهبر
فرآیند انتخاب رهبر برای عملکرد Raft اساسی است و تضمین میکند که کلاستر همیشه یک گره واحد و معتبر برای هماهنگی اقدامات دارد.
-
مهلت انتخاباتی: هر پیرو یک
election timeoutتصادفی (معمولاً ۱۵۰-۳۰۰ میلیثانیه) را حفظ میکند. اگر یک پیرو در این دوره زمانی هیچ ارتباطی (ضربان قلب یا AppendEntries RPC) از رهبر فعلی دریافت نکند، فرض میکند که رهبر از کار افتاده است یا یک جداسازی شبکه رخ داده است. -
تغییر به نامزد: پس از اتمام مهلت، پیرو به حالت
Candidateتغییر میکند.current termخود را افزایش میدهد، به خودش رأی میدهد و تایمر انتخابات خود را بازنشانی میکند. -
RPC درخواست رأی (RequestVote): نامزد سپس RPCهای
RequestVoteرا به تمامی سرورهای دیگر در کلاستر ارسال میکند. این RPC شاملcurrent termنامزد،candidateIdو اطلاعاتی دربارهlast log indexوlast log termآن است (در ادامه در مورد اهمیت این موضوع برای ایمنی بیشتر توضیح داده خواهد شد). -
قوانین رأیگیری: یک سرور رأی خود را به یک نامزد میدهد اگر:
-
current termآن کمتر یا مساوی با دوره نامزد باشد. - هنوز در دوره فعلی به نامزد دیگری رأی نداده باشد.
-
لاگ نامزد حداقل به اندازه لاگ خودش بهروز باشد. این امر با مقایسه ابتدا
last log term، سپسlast log indexدر صورت یکسان بودن دورهها تعیین میشود. یک نامزد "بهروز" است اگر لاگ آن شامل تمام ورودیهای commit شدهای باشد که لاگ رأیدهنده شامل میشود. این به عنوان محدودیت انتخاباتی شناخته میشود و برای ایمنی حیاتی است.
-
-
برنده شدن در انتخابات: یک نامزد در صورتی رهبر جدید میشود که از اکثریت سرورهای کلاستر برای همان دوره رأی دریافت کند. پس از انتخاب، رهبر جدید فوراً RPCهای
AppendEntries(ضربان قلب) را به تمامی سرورهای دیگر ارسال میکند تا اقتدار خود را تثبیت کرده و از انتخابات جدید جلوگیری کند. - تقسیم آرا و تلاشهای مجدد: ممکن است چندین نامزد به طور همزمان ظهور کنند که منجر به تقسیم آرا میشود، به طوری که هیچ نامزدی اکثریت را به دست نمیآورد. برای حل این مشکل، هر نامزد دارای یک مهلت انتخاباتی تصادفی است. اگر مهلت زمانی یک نامزد بدون برنده شدن در انتخابات یا شنیدن خبری از رهبر جدید منقضی شود، دوره خود را افزایش میدهد و یک انتخابات جدید را آغاز میکند. تصادفی بودن کمک میکند تا اطمینان حاصل شود که تقسیم آرا نادر و به سرعت حل میشوند.
-
کشف دورههای بالاتر: اگر یک نامزد (یا هر سروری) یک RPC با
termبالاتر ازcurrent termخود دریافت کند، فوراًcurrent termخود را به مقدار بالاتر بهروزرسانی میکند و به حالتfollowerبازمیگردد. این امر تضمین میکند که یک سرور با اطلاعات قدیمی هرگز سعی در تبدیل شدن به رهبر یا ایجاد اختلال در رهبر قانونی را ندارد.
۲. تکثیر لاگ
هنگامی که یک رهبر انتخاب شد، مسئولیت اصلی آن مدیریت لاگ تکثیر شده و اطمینان از سازگاری در سراسر کلاستر است. این شامل پذیرش دستورات کلاینت، اضافه کردن آنها به لاگ خود و تکثیر آنها به پیروها میشود.
- درخواستهای کلاینت: تمامی درخواستهای کلاینت (فرمانهایی که باید توسط ماشین حالت اجرا شوند) به رهبر هدایت میشوند. اگر یک کلاینت با یک پیرو تماس بگیرد، پیرو درخواست را به رهبر فعلی هدایت میکند.
-
اضافه کردن به لاگ رهبر: هنگامی که رهبر یک فرمان کلاینت را دریافت میکند، فرمان را به عنوان یک
log entryجدید به لاگ محلی خود اضافه میکند. هر ورودی لاگ شامل خود فرمان،termای که در آن دریافت شده وlog indexآن است. -
RPCهای AppendEntries: رهبر سپس RPCهای
AppendEntriesرا به تمامی پیروها ارسال میکند و از آنها میخواهد که ورودی جدید لاگ (یا دستهای از ورودیها) را به لاگهای خود اضافه کنند. این RPCها شامل موارد زیر هستند:-
term: دوره فعلی رهبر. -
leaderId: شناسه رهبر (برای هدایت کلاینتها توسط پیروها). -
prevLogIndex: اندیس ورودی لاگ بلافاصله قبل از ورودیهای جدید. -
prevLogTerm: دوره ورودیprevLogIndex. این دو (prevLogIndex,prevLogTerm) برای ویژگی تطابق لاگ حیاتی هستند. -
entries[]: ورودیهای لاگ برای ذخیره (برای ضربان قلب خالی است). -
leaderCommit:commitIndexرهبر (اندیس بالاترین ورودی لاگ که به عنوان commit شده شناخته میشود).
-
-
بررسی سازگاری (ویژگی تطابق لاگ): هنگامی که یک پیرو یک RPC
AppendEntriesرا دریافت میکند، یک بررسی سازگاری انجام میدهد. بررسی میکند که آیا لاگ آن شامل یک ورودی درprevLogIndexبا دورهای مطابق باprevLogTermاست یا خیر. اگر این بررسی شکست بخورد، پیرو RPCAppendEntriesرا رد میکند و به رهبر اطلاع میدهد که لاگ آن ناسازگار است. -
حل ناسازگاریها: اگر یک پیرو RPC
AppendEntriesرا رد کند، رهبرnextIndexرا برای آن پیرو کاهش میدهد و RPCAppendEntriesرا دوباره تلاش میکند.nextIndexاندیس ورودی لاگ بعدی است که رهبر به یک پیرو خاص ارسال خواهد کرد. این فرآیند ادامه مییابد تاnextIndexبه نقطهای برسد که لاگهای رهبر و پیرو مطابقت داشته باشند. هنگامی که تطابق پیدا شد، پیرو میتواند ورودیهای لاگ بعدی را بپذیرد و در نهایت لاگ خود را با لاگ رهبر سازگار کند. -
تعهد ورودیها (Committing Entries): یک ورودی زمانی تعهد شده (committed) در نظر گرفته میشود که رهبر با موفقیت آن را به اکثریت سرورها (از جمله خودش) تکثیر کرده باشد. پس از commit شدن، ورودی میتواند به ماشین حالت محلی اعمال شود. رهبر
commitIndexخود را بهروزرسانی میکند و این را در RPCهایAppendEntriesبعدی گنجانده تا پیروها را از ورودیهای commit شده مطلع کند. پیروهاcommitIndexخود را بر اساسleaderCommitرهبر بهروزرسانی میکنند و ورودیها را تا آن اندیس به ماشین حالت خود اعمال میکنند. - ویژگی کامل بودن رهبر (Leader Completeness Property): Raft تضمین میکند که اگر یک ورودی لاگ در یک دوره معین commit شود، تمامی رهبران بعدی نیز باید آن ورودی لاگ را داشته باشند. این ویژگی توسط محدودیت انتخاباتی اعمال میشود: یک نامزد تنها در صورتی میتواند در انتخابات برنده شود که لاگ آن حداقل به اندازه اکثریت سرورهای دیگر بهروز باشد. این امر از انتخاب رهبری که ممکن است ورودیهای commit شده را بازنویسی یا از دست بدهد، جلوگیری میکند.
۳. ویژگیها و تضمینهای ایمنی
استحکام Raft از چندین ویژگی ایمنی با دقت طراحی شده ناشی میشود که از ناسازگاریها جلوگیری کرده و یکپارچگی دادهها را تضمین میکنند:
- ایمنی انتخاب: در هر دوره معین، حداکثر یک رهبر میتواند انتخاب شود. این امر توسط مکانیزم رأیگیری اعمال میشود که در آن یک پیرو حداکثر یک رأی در هر دوره میدهد و یک نامزد به اکثریت آرا نیاز دارد.
- کامل بودن رهبر: اگر یک ورودی لاگ در یک دوره معین commit شده باشد، آنگاه آن ورودی در لاگهای تمامی رهبران بعدی نیز وجود خواهد داشت. این برای جلوگیری از از دست دادن دادههای commit شده حیاتی است و عمدتاً توسط محدودیت انتخاباتی تضمین میشود.
- ویژگی تطابق لاگ: اگر دو لاگ شامل یک ورودی با اندیس و دوره یکسان باشند، آنگاه لاگها در تمامی ورودیهای قبلی یکسان هستند. این امر بررسیهای سازگاری لاگ را ساده میکند و به رهبر اجازه میدهد تا لاگهای پیروها را به طور کارآمد بهروز کند.
- ایمنی commit: هنگامی که یک ورودی commit شد، هرگز بازگردانده یا بازنویسی نخواهد شد. این یک پیامد مستقیم از ویژگیهای کامل بودن رهبر و تطابق لاگ است. هنگامی که یک ورودی commit شد، به عنوان دائمی ذخیره شده در نظر گرفته میشود.
مفاهیم و مکانیزمهای کلیدی در Raft
فراتر از نقشها و فازهای عملیاتی، Raft بر چندین مفهوم اصلی برای مدیریت وضعیت و اطمینان از صحت تکیه دارد.
۱. دورهها (Terms)
یک term در Raft یک عدد صحیح با افزایش مداوم است. این به عنوان یک ساعت منطقی برای کلاستر عمل میکند. هر دوره با یک انتخابات آغاز میشود و اگر یک انتخابات موفقیتآمیز باشد، یک رهبر واحد برای آن دوره انتخاب میشود. دورهها برای شناسایی اطلاعات قدیمی و اطمینان از اینکه سرورها همیشه به جدیدترین اطلاعات ارجاع میدهند، حیاتی هستند:
-
سرورها
current termخود را در تمامی RPCها مبادله میکنند. -
اگر یک سرور، سرور دیگری را با
termبالاتر کشف کند،current termخود را بهروزرسانی کرده و به حالتfollowerبازمیگردد. -
اگر یک نامزد یا رهبر کشف کند که
termآن قدیمی است (کمتر ازtermسرور دیگر)، فوراً کنارهگیری میکند.
۲. ورودیهای لاگ
log جزء مرکزی Raft است. این یک دنباله مرتب از ورودیها است، که در آن هر log entry یک فرمان را برای اجرا توسط ماشین حالت نشان میدهد. هر ورودی شامل:
- فرمان: عملیات واقعی که باید انجام شود (به عنوان مثال، "set x=5"، "create user").
- دوره: دورهای که ورودی در آن بر روی رهبر ایجاد شده است.
- اندیس: موقعیت ورودی در لاگ. ورودیهای لاگ به شدت بر اساس اندیس مرتب شدهاند.
لاگ پایدار است، به این معنی که ورودیها قبل از پاسخ به کلاینتها در حافظه پایدار نوشته میشوند و در برابر از دست دادن دادهها در هنگام خرابیها محافظت میکنند.
۳. ماشین حالت
هر سرور در یک کلاستر Raft یک state machine را حفظ میکند. این یک جزء خاص برنامه است که ورودیهای لاگ commit شده را پردازش میکند. برای اطمینان از سازگاری، ماشین حالت باید قطعی (با توجه به همان حالت اولیه و دنباله دستورات، همیشه همان خروجی و حالت نهایی را تولید کند) و ایدئمپوتنت (اعمال یک فرمان مشابه چندین بار همان تأثیر را دارد که یک بار اعمال آن، که در مدیریت مجدد تلاشها به صورت ظریف کمک میکند، اگرچه تعهد لاگ Raft عمدتاً یک بار اعمال را تضمین میکند) باشد.
۴. اندیس تعهد (Commit Index)
commitIndex بالاترین اندیس ورودی لاگ است که به عنوان commit شده شناخته میشود. این بدان معناست که با اطمینان به اکثریت سرورها تکثیر شده و میتواند به ماشین حالت اعمال شود. رهبران commitIndex را تعیین میکنند، و پیروها commitIndex خود را بر اساس RPCهای AppendEntries رهبر بهروزرسانی میکنند. تمامی ورودیها تا commitIndex دائمی در نظر گرفته میشوند و نمیتوان آنها را بازگرداند.
۵. اسنپشاتها
با گذشت زمان، لاگ تکثیر شده میتواند بسیار بزرگ شود، فضای دیسک قابل توجهی را مصرف کرده و تکثیر و بازیابی لاگ را کند کند. Raft این مشکل را با snapshots حل میکند. یک اسنپشات یک نمایش فشرده از وضعیت ماشین حالت در یک نقطه زمانی خاص است. به جای نگهداری کل لاگ، سرورها میتوانند به صورت دورهای از وضعیت خود "اسنپشات" بگیرند، تمام ورودیهای لاگ تا نقطه اسنپشات را دور بریزند، و سپس اسنپشات را به پیروهای جدید یا عقبافتاده تکثیر کنند. این فرآیند کارایی را به طور قابل توجهی بهبود میبخشد:
- لاگ فشرده: میزان دادههای لاگ پایدار را کاهش میدهد.
- بازیابی سریعتر: سرورهای جدید یا خراب شده میتوانند به جای بازپخش کل لاگ از ابتدا، یک اسنپشات دریافت کنند.
-
RPC نصب اسنپشات (InstallSnapshot): Raft یک RPC
InstallSnapshotرا برای انتقال اسنپشاتها از رهبر به پیروها تعریف میکند.
اگرچه مؤثر است، اما ایجاد اسنپشات پیچیدگیهایی را به پیادهسازی اضافه میکند، به ویژه در مدیریت ایجاد همزمان اسنپشات، کوتاهسازی لاگ و انتقال.
پیادهسازی Raft: ملاحظات عملی برای استقرار جهانی
تبدیل طراحی ظریف Raft به یک سیستم مقاوم و آماده تولید، به ویژه برای مخاطبان جهانی و زیرساختهای متنوع، شامل پرداختن به چندین چالش مهندسی عملی است.
۱. تأخیر شبکه و جداسازیها در بافت جهانی
برای سیستمهای توزیعشده جهانی، تأخیر شبکه یک عامل مهم است. یک کلاستر Raft معمولاً نیاز دارد که اکثریتی از گرهها بر روی یک ورودی لاگ توافق کنند تا بتواند commit شود. در یک کلاستر که در سراسر قارهها گسترده شده است، تأخیر بین گرهها میتواند صدها میلیثانیه باشد. این به طور مستقیم بر موارد زیر تأثیر میگذارد:
- تأخیر Commit: زمان لازم برای commit شدن یک درخواست کلاینت میتواند توسط کندترین لینک شبکه به اکثریت کپیها دچار گلوگاه شود. استراتژیهایی مانند پیروهای فقط خواندنی (که برای خوانشهای قدیمی نیازی به تعامل رهبر ندارند) یا پیکربندی نصاب آگاه از موقعیت جغرافیایی (مثلاً ۳ گره در یک منطقه، ۲ گره در منطقهای دیگر برای یک کلاستر ۵ گرهای، که در آن اکثریت ممکن است در یک منطقه سریع واحد باشند) میتوانند این مشکل را کاهش دهند.
-
سرعت انتخاب رهبر: تأخیر بالا میتواند RPCهای
RequestVoteرا به تأخیر بیندازد و به طور بالقوه منجر به تقسیم آرای مکررتر یا زمانهای انتخابات طولانیتر شود. تنظیم مهلتهای انتخاباتی به گونهای که به طور قابل توجهی بزرگتر از تأخیر معمولی بین گرهها باشد، حیاتی است. - مدیریت جداسازی شبکه: شبکههای واقعی مستعد جداسازی هستند. Raft جداسازیها را به درستی با اطمینان از اینکه تنها پارتیشن حاوی اکثریتی از سرورها میتواند یک رهبر را انتخاب کند و پیشرفت کند، مدیریت میکند. پارتیشن اقلیت قادر به commit کردن ورودیهای جدید نخواهد بود، بنابراین از سناریوهای دوپارگی مغز جلوگیری میشود. با این حال، جداسازیهای طولانیمدت در یک تنظیمات توزیعشده جهانی میتواند منجر به عدم دسترسی در مناطق خاصی شود، که مستلزم تصمیمگیریهای معماری دقیق در مورد قرارگیری نصاب است.
۲. ذخیرهسازی پایدار و دوام
صحت Raft به شدت به پایداری لاگ و وضعیت آن بستگی دارد. قبل از اینکه یک سرور به یک RPC پاسخ دهد یا یک ورودی را به ماشین حالت خود اعمال کند، باید اطمینان حاصل کند که دادههای مربوطه (ورودیهای لاگ، current term، votedFor) در حافظه پایدار نوشته شده و fsync شدهاند (به دیسک فلاش شدهاند). این از از دست رفتن دادهها در صورت خرابی جلوگیری میکند. ملاحظات شامل موارد زیر است:
- عملکرد: نوشتن مکرر دیسک میتواند یک گلوگاه عملکرد باشد. دستهبندی نوشتنها و استفاده از SSDهای با کارایی بالا از بهینهسازیهای رایج هستند.
- قابلیت اطمینان: انتخاب یک راهکار ذخیرهسازی مقاوم و با دوام (دیسک محلی، ذخیرهسازی متصل به شبکه، ذخیرهسازی بلوکی ابری) حیاتی است.
- WAL (لاگ پیشنویس): اغلب، پیادهسازیهای Raft از یک لاگ پیشنویس برای دوام استفاده میکنند، مشابه پایگاههای داده، تا اطمینان حاصل کنند که تغییرات قبل از اعمال در حافظه، روی دیسک نوشته میشوند.
۳. تعامل کلاینت و مدلهای سازگاری
کلاینتها با ارسال درخواستها به رهبر با کلاستر Raft تعامل میکنند. مدیریت درخواستهای کلاینت شامل:
- کشف رهبر: کلاینتها به مکانیزمی برای یافتن رهبر فعلی نیاز دارند. این میتواند از طریق مکانیزم کشف سرویس، یک نقطه پایانی ثابت که هدایت میکند، یا با تلاش بر روی سرورها تا زمانی که یکی به عنوان رهبر پاسخ دهد، باشد.
- تلاش مجدد درخواستها: کلاینتها باید آماده باشند که در صورت تغییر رهبر یا وقوع خطای شبکه، درخواستها را دوباره ارسال کنند.
-
سازگاری خواندن: Raft عمدتاً سازگاری قوی را برای نوشتنها تضمین میکند. برای خواندنها، چندین مدل ممکن است:
- خواندنهای با سازگاری قوی: یک کلاینت میتواند از رهبر بخواهد تا قبل از ارائه یک عملیات خواندن، وضعیت خود را با ارسال یک ضربان قلب به اکثریتی از پیروانش، بهروز کند. این امر تازگی را تضمین میکند اما تأخیر را افزایش میدهد.
- خواندنهای با اجاره رهبر: رهبر میتواند برای یک دوره کوتاه از اکثریتی از گرهها "اجاره"ای را به دست آورد که در طی آن میداند هنوز رهبر است و میتواند عملیات خواندن را بدون نیاز به اجماع بیشتر ارائه دهد. این سریعتر است اما محدود به زمان است.
- خواندنهای قدیمی (از پیروها): خواندن مستقیم از پیروها میتواند تأخیر کمتری ارائه دهد اما خطر خواندن دادههای قدیمی را دارد اگر لاگ پیرو از رهبر عقب باشد. این برای برنامههایی که سازگاری نهایی برای خواندنها کافی است، قابل قبول است.
۴. تغییرات پیکربندی (عضویت کلاستر)
تغییر عضویت یک کلاستر Raft (افزودن یا حذف سرورها) یک عملیات پیچیده است که باید از طریق اجماع نیز انجام شود تا از ناسازگاریها یا سناریوهای دوپارگی مغز جلوگیری شود. Raft یک تکنیک به نام اجماع مشترک را پیشنهاد میکند:
- دو پیکربندی: در طول تغییر پیکربندی، سیستم به طور موقت با دو پیکربندی همپوشان عمل میکند: پیکربندی قدیمی (C_old) و پیکربندی جدید (C_new).
- حالت اجماع مشترک (C_old, C_new): رهبر یک ورودی لاگ خاص را پیشنهاد میدهد که نمایانگر پیکربندی مشترک است. هنگامی که این ورودی commit شود (نیاز به توافق اکثریتها در هم C_old و هم C_new دارد)، سیستم در یک حالت گذار قرار میگیرد. اکنون، تصمیمات نیازمند اکثریت از هر دو پیکربندی هستند. این تضمین میکند که در طول گذار، نه پیکربندی قدیمی و نه جدید نمیتوانند به صورت یکجانبه تصمیمگیری کنند، و از واگرایی جلوگیری میکند.
- انتقال به C_new: هنگامی که ورودی لاگ پیکربندی مشترک commit شد، رهبر یک ورودی لاگ دیگر را پیشنهاد میدهد که تنها پیکربندی جدید (C_new) را نشان میدهد. هنگامی که این ورودی دوم commit شد، پیکربندی قدیمی کنار گذاشته میشود و سیستم صرفاً تحت C_new عمل میکند.
- ایمنی: این فرآیند دو فازی شبیه به commit تضمین میکند که در هیچ نقطهای نمیتوان دو رهبر متناقض را انتخاب کرد (یکی تحت C_old، یکی تحت C_new) و سیستم در طول تغییر عملیاتی باقی میماند.
پیادهسازی صحیح تغییرات پیکربندی یکی از چالشبرانگیزترین بخشهای پیادهسازی Raft است، به دلیل موارد خاص متعدد و سناریوهای خرابی در طول حالت گذار.
۵. تست سیستمهای توزیعشده: رویکردی دقیق
تست یک الگوریتم اجماع توزیعشده مانند Raft به دلیل ماهیت غیرقطعی و تعدد حالتهای خرابی آن، فوقالعاده چالشبرانگیز است. تستهای واحد ساده کافی نیستند. تست دقیق شامل:
- تزریق خطا: معرفی سیستماتیک خرابیها مانند از کار افتادن گرهها، جداسازی شبکه، تأخیر پیامها و تغییر ترتیب پیامها. ابزارهایی مانند Jepsen به طور خاص برای این منظور طراحی شدهاند.
- تست مبتنی بر ویژگی: تعریف ناورداها و ویژگیهای ایمنی (مثلاً حداکثر یک رهبر در هر دوره، ورودیهای commit شده هرگز از بین نمیروند) و تست اینکه پیادهسازی این موارد را در شرایط مختلف حفظ میکند.
- بررسی مدل: برای بخشهای حیاتی الگوریتم، میتوان از تکنیکهای تأیید رسمی برای اثبات صحت استفاده کرد، اگرچه این کار بسیار تخصصی است.
- محیطهای شبیهسازی شده: اجرای تستها در محیطهایی که شرایط شبکه (تأخیر، از دست دادن بستهها) معمول استقرارهای جهانی را شبیهسازی میکنند.
موارد استفاده و کاربردهای دنیای واقعی
عملی بودن و فهمپذیری Raft منجر به پذیرش گسترده آن در اجزای مختلف زیرساختهای حیاتی شده است:
۱. فروشگاههای کلید-مقدار توزیعشده و تکثیر پایگاه داده
- etcd: یک جزء بنیادی Kubernetes، etcd از Raft برای ذخیره و تکثیر دادههای پیکربندی، اطلاعات کشف سرویس و مدیریت وضعیت کلاستر استفاده میکند. قابلیت اطمینان آن برای عملکرد صحیح Kubernetes حیاتی است.
- Consul: توسعه یافته توسط HashiCorp، Consul از Raft برای بکاند ذخیرهسازی توزیعشده خود استفاده میکند و کشف سرویس، بررسی سلامت و مدیریت پیکربندی را در محیطهای زیرساخت پویا امکانپذیر میسازد.
- TiKV: فروشگاه کلید-مقدار تراکنشگرا توزیعشده که توسط TiDB (یک پایگاه داده SQL توزیعشده) استفاده میشود، Raft را برای تکثیر دادهها و تضمینهای سازگاری خود پیادهسازی میکند.
- CockroachDB: این پایگاه داده SQL توزیعشده جهانی به طور گسترده از Raft برای تکثیر دادهها در چندین گره و منطقه جغرافیایی استفاده میکند، و دسترسی بالا و سازگاری قوی را حتی در مواجهه با خرابیهای در سطح منطقه تضمین میکند.
۲. کشف سرویس و مدیریت پیکربندی
Raft یک پایه ایدهآل برای سیستمهایی فراهم میکند که نیاز به ذخیره و توزیع فرادادههای حیاتی درباره سرویسها و پیکربندیها در سراسر یک کلاستر دارند. هنگامی که یک سرویس ثبت میشود یا پیکربندی آن تغییر میکند، Raft تضمین میکند که همه گرهها در نهایت بر روی وضعیت جدید توافق میکنند و بهروزرسانیهای پویا را بدون دخالت دستی امکانپذیر میسازد.
۳. هماهنگکنندههای تراکنش توزیعشده
برای سیستمهایی که نیازمند اتمی بودن در چندین عملیات یا سرویس هستند، Raft میتواند اساس هماهنگکنندههای تراکنش توزیعشده باشد، و تضمین کند که لاگهای تراکنش قبل از commit کردن تغییرات در میان شرکتکنندگان، به طور سازگار تکثیر میشوند.
۴. هماهنگی کلاستر و انتخاب رهبر در سایر سیستمها
فراتر از استفاده صریح از پایگاه داده یا فروشگاه کلید-مقدار، Raft اغلب به عنوان یک کتابخانه یا جزء اصلی برای مدیریت وظایف هماهنگی، انتخاب رهبران برای سایر فرآیندهای توزیعشده، یا ارائه یک کنترل پلن قابل اعتماد در سیستمهای بزرگتر تعبیه میشود. به عنوان مثال، بسیاری از راهکارهای ابری بومی (cloud-native) از Raft برای مدیریت وضعیت اجزای کنترل پلن خود بهره میبرند.
مزایا و معایب Raft
اگرچه Raft مزایای قابل توجهی ارائه میدهد، اما درک معاوضات آن ضروری است.
مزایا:
- فهمپذیری: هدف اصلی طراحی آن، که پیادهسازی، اشکالزدایی و استدلال درباره آن را آسانتر از الگوریتمهای اجماع قدیمیتر مانند Paxos میکند.
- سازگاری قوی: تضمینهای سازگاری قوی را برای ورودیهای لاگ commit شده ارائه میدهد، و یکپارچگی و قابلیت اطمینان دادهها را تضمین میکند.
-
تحمل خطا: میتواند خرابی اقلیتی از گرهها (تا
(N-1)/2خرابی در یک کلاسترNگرهای) را بدون از دست دادن دسترسی یا سازگاری تحمل کند. - عملکرد: در شرایط پایدار (بدون تغییر رهبر)، Raft میتواند به توان عملیاتی بالا دست یابد زیرا رهبر تمامی درخواستها را به صورت متوالی پردازش کرده و به صورت موازی تکثیر میکند، و از پهنای باند شبکه به طور کارآمد استفاده میکند.
- نقشهای تعریف شده خوب: نقشهای روشن (رهبر، پیرو، نامزد) و انتقال حالتها مدل ذهنی و پیادهسازی را ساده میکند.
- تغییرات پیکربندی: یک مکانیسم مقاوم (اجماع مشترک) برای افزودن یا حذف ایمن گرهها از کلاستر بدون به خطر انداختن سازگاری ارائه میدهد.
معایب:
- گلوگاه رهبر: تمامی درخواستهای نوشتن کلاینت باید از طریق رهبر انجام شود. در سناریوهایی با توان عملیاتی نوشتن بسیار بالا یا جایی که رهبران از لحاظ جغرافیایی از کلاینتها دور هستند، این میتواند به یک گلوگاه عملکرد تبدیل شود.
- تأخیر خواندن: دستیابی به خواندنهای با سازگاری قوی اغلب نیازمند ارتباط با رهبر است که به طور بالقوه تأخیر را اضافه میکند. خواندن از پیروها خطر خواندن دادههای قدیمی را دارد.
- نیاز به نصاب: نیاز دارد که اکثریتی از گرهها برای commit کردن ورودیهای جدید در دسترس باشند. در یک کلاستر ۵ گرهای، ۲ خرابی قابل تحمل است. اگر ۳ گره خراب شوند، کلاستر برای نوشتنها غیرقابل دسترس میشود. این میتواند در محیطهای بسیار تقسیم شده یا پراکنده جغرافیایی که حفظ اکثریت در سراسر مناطق دشوار است، چالشبرانگیز باشد.
- حساسیت شبکه: به شدت به تأخیر شبکه و جداسازیها حساس است که میتواند بر زمانهای انتخابات و توان عملیاتی کلی سیستم تأثیر بگذارد، به ویژه در استقرارهای گسترده توزیعشده.
- پیچیدگی تغییرات پیکربندی: اگرچه مقاوم است، مکانیزم اجماع مشترک (Joint Consensus) یکی از پیچیدهترین بخشهای الگوریتم Raft برای پیادهسازی صحیح و تست کامل است.
- نقطه واحد خرابی (برای نوشتنها): اگرچه برای خرابی رهبر تحملپذیر خطا است، اما اگر رهبر به طور دائمی از کار بیفتد و یک رهبر جدید نتواند انتخاب شود (مثلاً به دلیل جداسازی شبکه یا تعداد زیاد خرابیها)، سیستم نمیتواند در نوشتنها پیشرفت کند.
نتیجهگیری: تسلط بر اجماع توزیعشده برای سیستمهای جهانی مقاوم
الگوریتم Raft گواهی بر قدرت طراحی متفکرانه در سادهسازی مسائل پیچیده است. تأکید آن بر فهمپذیری، اجماع توزیعشده را همگانی کرده و به طیف وسیعتری از توسعهدهندگان و سازمانها اجازه میدهد تا سیستمهای با دسترسی بالا و تحملپذیر خطا را بدون گرفتار شدن در پیچیدگیهای رمزآلود رویکردهای قبلی بسازند.
از هماهنگی کلاسترهای کانتینری با Kubernetes (از طریق etcd) گرفته تا فراهم آوردن ذخیرهسازی داده مقاوم برای پایگاههای داده جهانی مانند CockroachDB، Raft یک نیروی کار بیصدا است که اطمینان میدهد دنیای دیجیتال ما سازگار و عملیاتی باقی میماند. پیادهسازی Raft کار آسانی نیست، اما وضوح مشخصات آن و غنای اکوسیستم پیرامون آن، آن را به تلاشی ارزشمند برای کسانی که متعهد به ساخت نسل بعدی زیرساختهای مقاوم و مقیاسپذیر هستند، تبدیل میکند.
بینشهای عملی برای توسعهدهندگان و معماران:
- اولویتبندی فهم: قبل از اقدام به پیادهسازی، زمان کافی را برای درک کامل هر قانون و انتقال حالت Raft اختصاص دهید. مقاله اصلی و توضیحات بصری منابع ارزشمندی هستند.
- بهرهگیری از کتابخانههای موجود: برای اکثر برنامهها، استفاده از پیادهسازیهای موجود و معتبر Raft (مانند etcd، کتابخانه Raft HashiCorp) را به جای ساخت از ابتدا در نظر بگیرید، مگر اینکه الزامات شما بسیار تخصصی باشد یا در حال انجام تحقیقات آکادمیک باشید.
- تست دقیق غیرقابل مذاکره است: تزریق خطا، تست مبتنی بر ویژگی و شبیهسازی گسترده سناریوهای خرابی برای هر سیستم اجماع توزیعشده حیاتی است. هرگز بدون شکستن کامل سیستم، فرض نکنید که "کار میکند".
- طراحی برای تأخیر جهانی: هنگام استقرار جهانی، قرارگیری نصاب، توپولوژی شبکه و استراتژیهای خواندن کلاینت خود را به دقت بررسی کنید تا هم برای سازگاری و هم برای عملکرد در مناطق جغرافیایی مختلف بهینهسازی کنید.
-
پایداری و دوام: اطمینان حاصل کنید که لایه ذخیرهسازی زیرین شما مقاوم است و عملیات
fsyncیا معادل آن به درستی برای جلوگیری از از دست رفتن دادهها در سناریوهای خرابی استفاده میشود.
همانطور که سیستمهای توزیعشده به تکامل خود ادامه میدهند، اصول تجسم یافته توسط Raft – وضوح، استحکام و تحمل خطا – همچنان سنگ بنای مهندسی نرمافزار قابل اعتماد باقی خواهند ماند. با تسلط بر Raft، خود را به ابزاری قدرتمند مجهز میکنید تا برنامههای مقاوم و مقیاسپذیر جهانی بسازید که میتوانند در برابر آشفتگی اجتنابناپذیر محاسبات توزیعشده مقاومت کنند.